﻿using System;
using System.Collections.Generic;
using System.Text;

namespace GxFramework
{
    public static class PoolManager
    {
        static readonly Stack<StringBuilder> stringBuildPool = new Stack<StringBuilder>(2);
        static readonly Dictionary<Type, ObjectPool> poolDict = new Dictionary<Type, ObjectPool>();
        static public StringBuilder GetStringBuilder(int capacity = 0)
        {
            StringBuilder sb;
            if (stringBuildPool.Count == 0)
                sb = new StringBuilder(capacity);
            else
                sb = stringBuildPool.Pop();
            return sb;
        }
        static public void ReaseStringBuilder(StringBuilder sb)
        {
            if(stringBuildPool.Count != 0 && ReferenceEquals(stringBuildPool.Peek(),sb))
                throw new Exception("[PoolManager] Internal error. Trying to put StringBuilder that is already released to pool.");
            sb.Length = 0;
            stringBuildPool.Push(sb);
        }
        static public int stringBuildPoolSize
        {
            get { return stringBuildPool.Count; }
            set
            {
                int len = value < 0 ? 0 : value;
                while (stringBuildPool.Count > len)
                    stringBuildPool.Pop();
            }
        }
        static public void SetPoolCallback(Type type,Action<object> onGet, Action<object> onRelease, Action<object> onDestroy)
        {
            var pool = GetPool(type,true);
            pool.SetCallback(onGet,onRelease,onDestroy);
        }
        static public void SetPoolCallback<T>(Action<object> onGet, Action<object> onRelease, Action<object> onDestroy)
        {
            SetPoolCallback(typeof(T), onGet, onRelease, onDestroy);
        }
        static ObjectPool GetPool(Type type, bool create)
        {
            ObjectPool ret=null;
            if (!poolDict.TryGetValue(type, out ret))
            {
                if (create)
                {
                    poolDict[type]=ret = new ObjectPool();
                }
            }
            return ret;
        }
        static public int GetPoolCountAll(Type type)
        {
            var pool = GetPool(type,false);
            return null == pool ? 0 : pool.countAll;
        }
        static public int GetPoolCountAll<T>()
        {
            return GetPoolCountAll(typeof(T));
        }
        static public int GetPoolCountActive(Type type)
        {
            var pool = GetPool(type, false);
            return null == pool ? 0 : pool.countActive;
        }
        static public int GetPoolCountActive<T>()
        {
            return GetPoolCountActive(typeof(T));
        }
        static public int GetPoolCountInactive(Type type)
        {
            var pool = GetPool(type, false);
            return null == pool ? 0 : pool.countInactive;
        }
        static public int GetPoolCountInactive<T>()
        {
            return GetPoolCountInactive(typeof(T));
        }
        static public object Get(Type type)
        {
            var pool = GetPool(type, false);
            return (null != pool && pool.countInactive != 0) ? pool.Get() : null;
        }
        static public T Get<T>() where T:new()
        {
            var type = typeof(T);
            var pool = GetPool(type, true);
           
            T ret = default(T);
            if (pool.countInactive == 0)
            {
                ret = new T();
                pool.OnSpawn(ret);
            }
            else
                ret = (T) pool.Get();
            return ret;
        }
        static public void Put(object item,Type type=null)
        {
            if (null == type)
                type = item.GetType();
            var pool = GetPool(type, true);
            pool.Put(item);
        }
        static public void Release(object item, Type type = null)
        {
            if (null == type)
                type = item.GetType();
            var pool = GetPool(type, true);
            pool.Release(item);
        }
        static public void Clear(Type type, int reserveCount=0)
        {
            var pool = GetPool(type, true);
            if (null != pool)
            {
                if (reserveCount <= 0)
                    pool.Clear();
                else
                    pool.Clear(reserveCount);
            }
        }
        static public void Clear<T>(int reserveCount = 0)
        {
            Clear(typeof(T));
        }
    }
}